package net.worldscale.xp.wslib.urm;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

class ImplURI extends URI {

    public static URI newInstance(String uri) throws BadUriException {
        try {
            return new ImplURI(uri);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BadUriException(e.getMessage());
        }
    }
    private String scheme;
    private String host;
    private String port;
    private String path;
    private String query;
    private String fragment;

    private ImplURI(String uri) throws BadUriException {
        parseUri(this, uri);
    }
    private static final int PHASE_MAIN = 1;
    private static final int PHASE_QUERY = 2;
    private static final int PHASE_FRAGMENT = 3;

    private static void parseUri(final ImplURI rlt, final String uri)
            throws BadUriException {
        final MyModel model = new MyModel();
        {
            final StringBuffer sbuf = new StringBuffer();
            int phase = PHASE_MAIN;
            final char[] chs = uri.toCharArray();
            final int len = chs.length;
            for (int i = 0; i < len; i++) {
                final char ch = chs[i];
                switch (phase) {
                    case PHASE_FRAGMENT:
                        sbuf.append(ch);
                        break;
                    case PHASE_QUERY:
                        if (ch == '#') {
                            rlt.query = sbuf.toString();
                            sbuf.setLength(0);
                            phase = PHASE_FRAGMENT;
                        } else {
                            sbuf.append(ch);
                        }
                        break;
                    case PHASE_MAIN:
                    default:
                        if (ch == '#') {
                            model.append(sbuf.toString());
                            sbuf.setLength(0);
                            phase = PHASE_FRAGMENT;
                        } else if (ch == '?') {
                            model.append(sbuf.toString());
                            sbuf.setLength(0);
                            phase = PHASE_QUERY;
                        } else if (ch == ':') {
                            model.append(sbuf.toString());
                            model.append(ch);
                            sbuf.setLength(0);
                        } else if (ch == '/') {
                            model.append(sbuf.toString());
                            model.append(ch);
                            sbuf.setLength(0);
                        } else {
                            sbuf.append(ch);
                        }
                }
            }
            // 收尾
            if (0 < sbuf.length()) {
                switch (phase) {
                    case PHASE_FRAGMENT:
                        rlt.fragment = sbuf.toString();
                        break;
                    case PHASE_QUERY:
                        rlt.query = sbuf.toString();
                        break;
                    case PHASE_MAIN:
                    default:
                        model.append(sbuf.toString());
                }
            }
        }
        // parse uriWithoutQF
        {
            final String mdstr = model.getModelString();
            final String shorModelStr;
            if (mdstr.endsWith("_")) {
                shorModelStr = (mdstr + "/_/_/_/_").substring(0, 8);
            } else if (mdstr.endsWith("/")) {
                shorModelStr = (mdstr + "_/_/_/_/").substring(0, 8);
            } else {
                shorModelStr = (mdstr + "????????").substring(0, 8);
            }
            final MyModelReader mr = sModelReaderReg.getModelReader(shorModelStr);
            try {
                mr.readModel(model, rlt);
            } catch (Exception e) {
                e.printStackTrace();
                throw new BadUriException("This is a bad URI: [" + shorModelStr + "]" + uri);
            }
        }
    }
    private final static MyModelReaderRegistrar sModelReaderReg = new MyModelReaderRegistrar();

    private final static class MyModelReaderRegistrar {

        private final Hashtable<String, MyModelReader> mTable;

        public MyModelReaderRegistrar() {
            mTable = new Hashtable<String, MyModelReader>();
            this.registerModelReader(new MyModelReader() {

                @Override
                public String getModelString() {
                    return "_://_:_/";
                }

                @Override
                public void readModel(MyModel model, ImplURI rlt) {
                    rlt.scheme = model.stringAt(0);
                    rlt.host = model.stringAt(4);
                    rlt.port = model.stringAt(6);
                    rlt.path = model.stringFrom(7);
                }
            });
            this.registerModelReader(new MyModelReader() {

                @Override
                public String getModelString() {
                    return "_://_/_/";
                }

                @Override
                public void readModel(MyModel model, ImplURI rlt) {
                    rlt.scheme = model.stringAt(0);
                    rlt.host = model.stringAt(4);
                    rlt.path = model.stringFrom(5);
                }
            });
            this.registerModelReader(new MyModelReader() {

                @Override
                public String getModelString() {
                    return "/_/_/_/_";
                }

                @Override
                public void readModel(MyModel model, ImplURI rlt) {
                    rlt.path = model.stringFrom(0);
                }
            });
            this.registerModelReader(new MyModelReader() {

                @Override
                public String getModelString() {
                    return "_:_/_/_/";
                }

                @Override
                public void readModel(MyModel model, ImplURI rlt) {
                    rlt.scheme = model.stringAt(0);
                    rlt.path = model.stringFrom(2);
                }
            });
            this.registerModelReader(new MyModelReader() {

                @Override
                public String getModelString() {
                    return "_:///_/_";
                }

                @Override
                public void readModel(MyModel model, ImplURI rlt) {
                    rlt.scheme = model.stringAt(0);
                    rlt.host = "";
                    rlt.path = model.stringFrom(4);
                }
            });


            this.registerModelReader(new MyModelReader() {

                @Override
                public String getModelString() {
                    return "_/_/_/_/";
                }

                @Override
                public void readModel(MyModel model, ImplURI rlt) {
                    rlt.path = model.stringFrom(0);
                }
            });

        }

        public void registerModelReader(MyModelReader mr) {
            mTable.put(mr.getModelString(), mr);
        }

        public MyModelReader getModelReader(String mdstr) {
            return mTable.get(mdstr);
        }
    }

    private static abstract class MyModelReader {

        public abstract String getModelString();

        public abstract void readModel(final MyModel model, final ImplURI rlt);
    }

    private static class MyModel {

        private final Vector<String> mStrList = new Vector<String>();

        public void append(String str) {
            if (str != null) {
                if (str.length() > 0) {
                    mStrList.addElement(str);
                }
            }
        }

        public void append(char ch) {
            mStrList.addElement(String.valueOf(ch));
        }

        public String getModelString() {
            final StringBuffer sbuf = new StringBuffer();
            for (final Enumeration<String> enu = this.mStrList.elements(); enu.hasMoreElements();) {
                final String str = enu.nextElement();
                if (":".equals(str) || "/".equals(str)) {
                    sbuf.append(str);
                } else {
                    sbuf.append('_');
                }
            }
            return sbuf.toString();
        }

        public String stringAt(int index) {
            return this.mStrList.elementAt(index);
        }

        public String stringFrom(int index) {
            final StringBuffer sbuf = new StringBuffer();
            final int len = this.mStrList.size();
            for (int i = index; i < len; i++) {
                sbuf.append(this.mStrList.elementAt(i));
            }
            return sbuf.toString();
        }
    }

    @Override
    public String getFragment() {
        return fragment;
    }

    @Override
    public String getHost() {
        return host;
    }

    @Override
    public String getPath() {
        return path;
    }

    @Override
    public String getPort() {
        return port;
    }

    @Override
    public String getQuery() {
        return query;
    }

    @Override
    public String getScheme() {
        return scheme;
    }

    @Override
    public String toString() {
        final StringBuffer sbuf = new StringBuffer();
        if (scheme != null) {
            sbuf.append(scheme + ':');
        }
        if (host != null) {
            sbuf.append("//" + host);
        }
        if (port != null) {
            sbuf.append(':' + port);
        }
        if (path != null) {
            sbuf.append(path);
        }
        if (query != null) {
            sbuf.append('?' + query);
        }
        if (fragment != null) {
            sbuf.append('#' + fragment);
        }
        return sbuf.toString();
    }
}
